From 63d1f8074287e4001b74b2f52018f32fe0b9cb7b Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Sat, 13 Feb 2016 03:56:12 +0100 Subject: [PATCH] css: Add support for '*' and '/' to calc() More tests are included. --- gtk/gtkcsscalcvalue.c | 101 ++++++++++++++++++++++- testsuite/css/parser/Makefile.am | 3 + testsuite/css/parser/calc-errors.css | 59 +++++++++++++ testsuite/css/parser/calc-errors.errors | 15 ++++ testsuite/css/parser/calc-errors.ref.css | 0 testsuite/css/parser/calc.css | 4 + testsuite/css/parser/calc.ref.css | 4 + 7 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 testsuite/css/parser/calc-errors.css create mode 100644 testsuite/css/parser/calc-errors.errors create mode 100644 testsuite/css/parser/calc-errors.ref.css diff --git a/gtk/gtkcsscalcvalue.c b/gtk/gtkcsscalcvalue.c index 8df2a67ad9..2bcb5455fc 100644 --- a/gtk/gtkcsscalcvalue.c +++ b/gtk/gtkcsscalcvalue.c @@ -316,13 +316,94 @@ gtk_css_calc_value_new_sum (GtkCssValue *value1, return gtk_css_value_new_from_array (array); } +GtkCssValue * +gtk_css_calc_value_parse_value (GtkCssParser *parser, + GtkCssNumberParseFlags flags) +{ + if (_gtk_css_parser_has_prefix (parser, "calc")) + { + _gtk_css_parser_error (parser, "Nested calc() expressions are not allowed."); + return NULL; + } + + return _gtk_css_number_value_parse (parser, flags); +} + +static gboolean +is_number (GtkCssValue *value) +{ + return gtk_css_number_value_get_dimension (value) == GTK_CSS_DIMENSION_NUMBER + && !gtk_css_number_value_has_percent (value); +} + +GtkCssValue * +gtk_css_calc_value_parse_product (GtkCssParser *parser, + GtkCssNumberParseFlags flags) +{ + GtkCssValue *result, *value, *temp; + GtkCssNumberParseFlags actual_flags; + + actual_flags = flags | GTK_CSS_PARSE_NUMBER; + + result = gtk_css_calc_value_parse_value (parser, actual_flags); + if (result == NULL) + return NULL; + + while (_gtk_css_parser_begins_with (parser, '*') || _gtk_css_parser_begins_with (parser, '/')) + { + if (actual_flags != GTK_CSS_PARSE_NUMBER && !is_number (result)) + actual_flags = GTK_CSS_PARSE_NUMBER; + + if (_gtk_css_parser_try (parser, "*", TRUE)) + { + value = gtk_css_calc_value_parse_product (parser, actual_flags); + if (value == NULL) + goto fail; + if (is_number (value)) + temp = gtk_css_number_value_multiply (result, _gtk_css_number_value_get (value, 100)); + else + temp = gtk_css_number_value_multiply (value, _gtk_css_number_value_get (result, 100)); + _gtk_css_value_unref (value); + _gtk_css_value_unref (result); + result = temp; + } + else if (_gtk_css_parser_try (parser, "/", TRUE)) + { + value = gtk_css_calc_value_parse_product (parser, GTK_CSS_PARSE_NUMBER); + if (value == NULL) + goto fail; + temp = gtk_css_number_value_multiply (result, 1.0 / _gtk_css_number_value_get (value, 100)); + _gtk_css_value_unref (value); + _gtk_css_value_unref (result); + result = temp; + } + else + { + g_assert_not_reached (); + goto fail; + } + } + + if (is_number (result) && !(flags & GTK_CSS_PARSE_NUMBER)) + { + _gtk_css_parser_error (parser, "calc() product term has no units"); + goto fail; + } + + return result; + +fail: + _gtk_css_value_unref (result); + return NULL; +} + GtkCssValue * gtk_css_calc_value_parse_sum (GtkCssParser *parser, GtkCssNumberParseFlags flags) { GtkCssValue *result; - result = _gtk_css_number_value_parse (parser, flags); + result = gtk_css_calc_value_parse_product (parser, flags); if (result == NULL) return NULL; @@ -332,17 +413,22 @@ gtk_css_calc_value_parse_sum (GtkCssParser *parser, if (_gtk_css_parser_try (parser, "+", TRUE)) { - next = _gtk_css_number_value_parse (parser, flags); + next = gtk_css_calc_value_parse_product (parser, flags); + if (next == NULL) + goto fail; } else if (_gtk_css_parser_try (parser, "-", TRUE)) { - temp = _gtk_css_number_value_parse (parser, flags); + temp = gtk_css_calc_value_parse_product (parser, flags); + if (temp == NULL) + goto fail; next = gtk_css_number_value_multiply (temp, -1); _gtk_css_value_unref (temp); } else { g_assert_not_reached (); + goto fail; } temp = gtk_css_number_value_add (result, next); @@ -352,6 +438,10 @@ gtk_css_calc_value_parse_sum (GtkCssParser *parser, } return result; + +fail: + _gtk_css_value_unref (result); + return NULL; } GtkCssValue * @@ -360,6 +450,11 @@ gtk_css_calc_value_parse (GtkCssParser *parser, { GtkCssValue *value; + /* This confuses '*' and '/' so we disallow backwards compat. */ + flags &= ~GTK_CSS_NUMBER_AS_PIXELS; + /* This can only be handled at compute time, we allow '-' after all */ + flags &= ~GTK_CSS_POSITIVE_ONLY; + if (!_gtk_css_parser_try (parser, "calc(", TRUE)) { _gtk_css_parser_error (parser, "Expected 'calc('"); diff --git a/testsuite/css/parser/Makefile.am b/testsuite/css/parser/Makefile.am index fcef07213a..54c9506a5f 100644 --- a/testsuite/css/parser/Makefile.am +++ b/testsuite/css/parser/Makefile.am @@ -228,6 +228,9 @@ test_data = \ box-shadow.ref.css \ calc.css \ calc.ref.css \ + calc-errors.css \ + calc-errors.ref.css \ + calc-errors.errors \ calc-simple.css \ calc-simple.ref.css \ close-at-end-of-file.css \ diff --git a/testsuite/css/parser/calc-errors.css b/testsuite/css/parser/calc-errors.css new file mode 100644 index 0000000000..a109e62400 --- /dev/null +++ b/testsuite/css/parser/calc-errors.css @@ -0,0 +1,59 @@ +a { + margin-left: calc(calc(1px)); +} + +b { + margin-left: calc(1px + 2s); +} + +c { + margin-left: calc(1px - 2s); +} + +d { + margin-left: calc(2 * 3); +} + +e { + margin-left: calc(2px * 3px); +} + +f { + margin-left: calc(2 / 3px); +} + +g { + margin-left: calc(2 / 3); +} + +h { + margin-left: calc(2px / 3px); +} + +i { + margin-left: calc(error); +} + +j { + margin-left: calc(1px + error); +} + +k { + margin-left: calc(1px - error); +} + +l { + margin-left: calc(1 * error); +} + +m { + margin-left: calc(1 / error); +} + +n { + margin-left: calc(1px * error); +} + +o { + margin-left: calc(1px / error); +} diff --git a/testsuite/css/parser/calc-errors.errors b/testsuite/css/parser/calc-errors.errors new file mode 100644 index 0000000000..891991dd35 --- /dev/null +++ b/testsuite/css/parser/calc-errors.errors @@ -0,0 +1,15 @@ +calc-errors.css:2: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +calc-errors.css:6: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +calc-errors.css:10: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +calc-errors.css:14: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +calc-errors.css:18: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +calc-errors.css:22: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +calc-errors.css:26: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +calc-errors.css:30: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +calc-errors.css:34: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +calc-errors.css:38: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +calc-errors.css:42: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +calc-errors.css:46: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +calc-errors.css:50: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +calc-errors.css:54: error: GTK_CSS_PROVIDER_ERROR_SYNTAX +calc-errors.css:58: error: GTK_CSS_PROVIDER_ERROR_SYNTAX diff --git a/testsuite/css/parser/calc-errors.ref.css b/testsuite/css/parser/calc-errors.ref.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testsuite/css/parser/calc.css b/testsuite/css/parser/calc.css index 6e7bf54700..5406818e47 100644 --- a/testsuite/css/parser/calc.css +++ b/testsuite/css/parser/calc.css @@ -17,3 +17,7 @@ d { e { border-left-width: calc(1px + 1px); } + +f { + background-size: calc(2 * 3px + 4px * 5 - 6px / 3); +} diff --git a/testsuite/css/parser/calc.ref.css b/testsuite/css/parser/calc.ref.css index 4e858f70c8..f0c2bbec07 100644 --- a/testsuite/css/parser/calc.ref.css +++ b/testsuite/css/parser/calc.ref.css @@ -17,3 +17,7 @@ d { e { border-left-width: 2px; } + +f { + background-size: 24px; +} -- 2.30.2